﻿using Autofac;
using Autofac.Core.Registration;
using Autofac.Features.Variance;
using AutoRegistDependency.Abstract;
using AutoRegistDependency.Attributes;
using AutoRegistDependency.Component;
using AutoRegistDependency.Enum;
using AutoRegistDependency.Implement;
using AutoRegistDependency.Interface;
using AutoRegistDependency.Module;
using AutoRegistDependency.RegistrationHandler;
using AutoRegistDependency.Utils;
using Microsoft.Extensions.Hosting;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Data.Common;
using System.Linq;
using System.Reflection;
using Module = Autofac.Module;

namespace AutoRegistDependency
{
    /// <summary>
    /// 注册类 所有的注册逻辑都在此类实现
    /// </summary>
    public class Register : Autofac.Module
    {
        private readonly List<Assembly> assemblies = new List<Assembly>();
        private LifeTimeType globalLifeTime = LifeTimeType.InstancePerLifetimeScope;

        public LifeTimeType GlobalLifeTime
        {
            get => globalLifeTime;
            set
            {
                if (value == LifeTimeType.Default)
                {
                    throw new ArgumentException("GlobalLifeTime can not be set to Default");
                }

                globalLifeTime = value;
            }
        }

        private PropertyInjectType globalPropertyInjectType = PropertyInjectType.All;

        public PropertyInjectType GlobalPropertyInjectType
        {
            get => globalPropertyInjectType;
            set
            {
                if (value == PropertyInjectType.Default)
                {
                    throw new ArgumentException("GlobalPropertyInjectType can not be set to Default");
                }

                globalPropertyInjectType = value;
            }
        }

        private RegistrationType globalRegistrationType = RegistrationType.All;

        public RegistrationType GlobalRegistrationType
        {
            get => globalRegistrationType;
            set
            {
                if (value == RegistrationType.Default)
                {
                    throw new ArgumentException("GlobalRegistrationType can not be set to Default");
                }

                globalRegistrationType = value;
            }
        }

        public bool EnableEventBus { get; set; } = true;
        public bool EnableQuartz { get; set; } = true;
        private Type defaultEventBusImpl = typeof(EventBus);
        public IRegistrationInitializer RegistrationInitializer { get; set; } = new DefaultRegistrationInitializer();

        /// <summary>
        /// 注册信息表
        /// </summary>
        public ConcurrentDictionary<Type, List<ComponentDefinition>> TypeMapInfo { get; private set; } =
            new ConcurrentDictionary<Type, List<ComponentDefinition>>();

        private readonly HostBuilderContext hostBuilderContext;
        private bool hasBeanPostProcessor = false;

        /// <summary>
        /// ctor
        /// </summary>
        /// <param name="hostBuilderContext">host上下文</param>
        public Register(HostBuilderContext hostBuilderContext)
        {
            this.hostBuilderContext = hostBuilderContext;
            assemblies = AutoFacHelper.GetAssemblies();
        }

        /// <summary>
        /// ctor
        /// </summary>
        /// <param name="hostBuilderContext">host上下文</param>
        /// <param name="assemblies">指定扫描程序集</param>
        public Register(HostBuilderContext hostBuilderContext, params Assembly[] assemblies)
        {
            this.hostBuilderContext = hostBuilderContext;
            this.assemblies = assemblies.ToList();
        }

        /// <summary>
        /// ctor
        /// </summary>
        /// <param name="hostBuilderContext">host上下文</param>
        /// <param name="assemblyNames">指定程序集字符串名称</param>
        public Register(HostBuilderContext hostBuilderContext, params string[] assemblyNames)
        {
            this.hostBuilderContext = hostBuilderContext;
            foreach (string name in assemblyNames)
            {
                Assembly assembly = Assembly.LoadFrom(name);
                if (assembly != null)
                {
                    assemblies.Add(assembly);
                }
            }
        }

        /// <summary>
        /// ctor
        /// </summary>
        /// <param name="hostBuilderContext">host上下文</param>
        /// <param name="types">被扫描的程序集所有的类</param>
        public Register(HostBuilderContext hostBuilderContext, params Type[] types)
        {
            this.hostBuilderContext = hostBuilderContext;
            foreach (Type type in types)
            {
                Assembly assembly = Assembly.GetAssembly(type);
                if (assembly != null)
                {
                    assemblies.Add(assembly);
                }
            }
        }

        /// <summary>
        /// 设置事件总线的类型
        /// </summary>
        /// <param name="eventBusType"></param>
        /// <returns></returns>
        /// <exception cref="ArgumentException"></exception>
        public Register SetEventBusType(Type eventBusType)
        {
            if (!eventBusType.IsClass || typeof(IEventBus).IsAssignableFrom(eventBusType))
            {
                throw new ArgumentException($"eventBusType must be Implement IEventBus!!");
            }
            else
            {
                this.defaultEventBusImpl = eventBusType;
            }

            return this;
        }

        protected override void Load(ContainerBuilder builder)
        {
            RegisterType(builder);
        }

        /// <summary>
        /// 注册所有带有特性的类
        /// </summary>
        /// <param name="containerBuilder">容器</param>
        public void RegisterType(ContainerBuilder containerBuilder)
        {
            if (EnableEventBus)
            {
                containerBuilder.RegisterSource(new ContravariantRegistrationSource());
                containerBuilder.RegisterType(defaultEventBusImpl).AsSelf().AsImplementedInterfaces().SingleInstance();
            }

            if (EnableQuartz)
            {
                containerBuilder.RegisterModule(new QuartzAutofacFactoryModule());
            }

            TypeMapInfo = RegistrationInitializer.CreateDefinitionDictionary(assemblies, GlobalLifeTime,
                GlobalPropertyInjectType, GlobalRegistrationType);
            List<ComponentDefinition> components = TypeMapInfo.Values.SelectMany(t => t).ToList();
            hasBeanPostProcessor = components.Any(t => t.ComponentType == ComponentType.BeanPostProcessor);
            containerBuilder.RegisterInstance(this).SingleInstance();
            var componentHandler = CreateCompleteHandler();
            foreach (var component in components
                         .OrderBy(t => t.Conditions.Count())
                         .ThenBy(t => t.Order)
                         .ThenBy(t => t.Name))
            {
                foreach (IGrouping<bool, Type> group in component.RegisterAs.GroupBy(t => t.IsInterface))
                {
                    var r = componentHandler.StartRegistration(containerBuilder, component, group.Key, group.ToArray(),
                        hasBeanPostProcessor, hostBuilderContext);
                }
            }
        }

        /// <summary>
        /// 构造一个完整的责任链handler
        /// </summary>
        /// <returns></returns>
        private AbstractRegistrationHandler CreateCompleteHandler()
        {
            ComponentHandler componentHandler = new ComponentHandler();
            AggregateServiceHandler aggregateServiceHandler = new AggregateServiceHandler();
            BeanHandler beanHandler = new BeanHandler();
            GenericComponentHandler genericComponentHandler = new GenericComponentHandler();
            GenericAggregateServiceHandler genericAggregateServiceHandler = new GenericAggregateServiceHandler();
            GenericBeanHandler genericBeanHandler = new GenericBeanHandler();
            componentHandler.Next(genericComponentHandler);
            genericComponentHandler.Next(aggregateServiceHandler);
            aggregateServiceHandler.Next(genericAggregateServiceHandler);
            genericAggregateServiceHandler.Next(beanHandler);
            beanHandler.Next(genericBeanHandler);
            return componentHandler;
        }
    }
}